package com.bondar.classloader;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.logging.Logger;

public class CustomCL extends ClassLoader {

	public CustomCL(ClassLoader classLoader) {
		super(classLoader);
	}

	public CustomCL() {
	}

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {

		String javaFile = name.replace('.', '/') + ".java";
		String classFile = name.replace('.', '/') + ".class";
		
		byte[] raw = null;

		try {
			
			if (!compile(javaFile)) {
				throw new ClassNotFoundException(name);
			}
			
			raw = readSource(classFile);
			
		} catch (IOException e) {
			e.printStackTrace();
		}

		return defineClass(name, raw, 0, raw.length);
	}

	/**
	 * Compile .class file from source
	 * 
	 * @param fileName
	 *            URI
	 * @return true if compile successfully
	 * @throws IOException
	 */
	public boolean compile(String fileName) throws IOException {

		Logger.getAnonymousLogger().info("Recompile source: " + fileName);
		Process process = null;

		process = Runtime.getRuntime().exec("javac " + fileName);

		try {
			process.waitFor();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		return process.exitValue() == 0;
	}

	/**
	 * Read bytes from .class file
	 * 
	 * @param fileName
	 *            URI
	 * @return array of bytes
	 * @throws IOException 
	 */
	private byte[] readSource(String fileName) throws IOException  {
		
		Logger.getAnonymousLogger().info("readSource: " + fileName);
		byte[] source = null;

		File file = new File(fileName);
		FileInputStream fis = null;

		try {

			fis = new FileInputStream(file);
			source = new byte[(int) file.length()];

			int read = fis.read(source);
			if (read != file.length())
				throw new IOException("Cannot read file: " + fileName);

		} finally {
			if (fis != null)
				fis.close();
		}

		return source;
	}

	@Override
	protected Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {

		Logger.getAnonymousLogger().info(
				"run " + this.getClass().getSimpleName());

		if (Test.NAME.equals(name)) {
			Logger.getAnonymousLogger().info(Test.NAME + " == " + name);
			return findClass(name);
		}

		return super.loadClass(name, true);
	}

}
